package evaluator;

import java.awt.Color;
import java.awt.Point;
import java.util.List;
import java.util.Map;
import java.util.Set;

import util.ColorUtils;

import model.GameModel;
import model.entities.Direction;
import model.entities.IEntity;
import model.entities.LaserBeam;
import model.entities.LaserBeamSegment;
import model.entities.LaserSource;
import model.entities.targets.ITarget;
import model.gameBoard.GameBoard;

/**
 * Evaluates the game state and creates lasers
 * 
 * @author ben
 *
 */
public class Evaluator {
	
	private GameModel model;
	
	public Evaluator(GameModel model) {
		this.model = model;
	}

	public void evaluate() {
		
		model.clearLaserBeamList(); // reset beam list
		model.resetTargets();
		GameBoard gameBoard = model.getGameBoard();
		Map<Point, LaserSource> sourceMap = gameBoard.getSourceMap();
		int width = gameBoard.getDimension().width;
		int height = gameBoard.getDimension().height;
		
		Set<Point> keySet = sourceMap.keySet();
		
		for (Point cell : keySet) {
			
			Point currentCell = new Point(cell);
			LaserSource source = sourceMap.get(cell);
			LaserBeam laserBeam = new LaserBeam(source.getColor());
			model.getLaserBeamList().add(laserBeam);
			Direction sourceDirection = source.getDirecton();
			Direction currentDirection = sourceDirection;

			Point startCell = new Point(currentCell);
			boolean reachedEdge = false;
			
			while( currentDirection!= null && !reachedEdge) {
				
				IEntity entity = gameBoard.getEntityAtCell(currentCell);
				
				switch (currentDirection) {
				
				case TOP : 
					if (currentCell.y == 0) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(0, -1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
				
				case TOP_RIGHT :
					if (currentCell.x == (height - 1) || currentCell.y == 0) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(1, -1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case RIGHT :
					if (currentCell.x == (width - 1)) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(1, 0);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case BOTTOM_RIGHT :
					if ((currentCell.x == width - 1) || (currentCell.y == height - 1)) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(1, 1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case BOTTOM :
					if (currentCell.y == height - 1) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(0, 1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case BOTTOM_LEFT :
					if (currentCell.x == 0 || currentCell.y == height - 1) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(-1, 1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case LEFT :
					if (currentCell.x == 0) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(-1, 0);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				case TOP_LEFT :
					if (currentCell.x == 0 || currentCell.y == 0) {
						reachedEdge = true;
						break;
					}
					currentCell.translate(-1, -1);
					entity = gameBoard.getEntityAtCell(currentCell);
					break;
					
				}
				
				if (entity == null && !reachedEdge) // empty cell; keep following same direction
					continue;
				
				Direction newDirection = currentDirection;

				if (entity != null)
					newDirection = entity.handleLaserBeam(currentDirection.invert(), source.getColor());
				
				LaserBeamSegment beamSegment = new LaserBeamSegment(new Point(startCell), new Point(currentCell), currentDirection);
				// TODO: smells like bad logic to me. Look at this later. ben
				beamSegment.setHitEdge(reachedEdge && (newDirection != null || entity == null) || startCell.equals(currentCell)); 
				laserBeam.AddSegment(beamSegment);
				startCell = new Point(currentCell);
				
				currentDirection = newDirection;
				
			}
			
		}
		
		mixConincidentSegments();
		checkForWinState();
		
	}

	private void mixConincidentSegments() {
		
		List<LaserBeam> beams = model.getLaserBeamList();
		
		for (LaserBeam currentBeam : beams) {
			for (LaserBeam checkBeam : beams) {
				int lastIndex = checkBeam.getSegmentList().size() - 1;
				LaserBeamSegment sourceSegment = currentBeam.getSegmentList().get(0);				
				LaserBeamSegment endSegment = checkBeam.getSegmentList().get(lastIndex);
				
				if (sourceSegment.getStartCell().equals(endSegment.getEndCell())
						&& sourceSegment.getEndCell().equals(endSegment.getStartCell())) {
					Color c1 = currentBeam.getColor();
					Color c2 = checkBeam.getColor();
					Color mix = ColorUtils.mixColors(c1, c2);
					currentBeam.setColor(mix);
					checkBeam.setColor(mix);
				}
				
			}
		}
		
	}
	
	private void checkForWinState() {
		Map<Point, ITarget> targetMap = model.getGameBoard().getTargetMap();
		Set<Point> targetSet = targetMap.keySet();
		
		boolean won = true;
		
		for (Point cell : targetSet) {
			ITarget target = targetMap.get(cell);
			if (!target.isSatisfied())
				won = false;
		}
		model.setWon(won);
	}
	
}
